package com.tuananh.radnom.model.netpoller;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.Calendar;
import java.util.HashMap;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteException;
import org.apache.commons.exec.ExecuteWatchdog;
import org.apache.commons.exec.PumpStreamHandler;

import com.tuananh.radnom.model.NetInterface;
import com.tuananh.radnom.model.utils.TokenReplace;

@XmlRootElement
public class Command {
	String prefix;
	String suffix;
	String name;
	String path;
	String usage = null;

	@XmlAttribute
	public String getPrefix() {
		return prefix;
	}

	public void setPrefix(String prefix) {
		this.prefix = prefix;
	}

	@XmlAttribute
	public String getSuffix() {
		return suffix;
	}

	public void setSuffix(String suffix) {
		this.suffix = suffix;
	}

	@XmlAttribute
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@XmlAttribute
	public String getPath() {
		return path;
	}

	public void setPath(String path) {
		this.path = path;
	}

	public String getUsage() {
		if (usage == null) {

		}
		return usage;
	}

	public void setUsage(String usage) {
		this.usage = usage;
	}

	static class StreamHandler extends Thread {
		public boolean finished = false;
		InputStream inpStr;
		String strType;
		String result = "";

		public StreamHandler() {
		};

		public StreamHandler(InputStream inpStr, String strType) {
			this.inpStr = inpStr;
			this.strType = strType;
		}

		public void run() {
			try {
				InputStreamReader inpStrd = new InputStreamReader(inpStr);
				BufferedReader buffRd = new BufferedReader(inpStrd);

				int read = 0;
				char[] buff = new char[256];
				while (read >= 0) {
					int avail = inpStr.available();
					if (avail > 0) {
						read = buffRd.read(buff, 0,
								Math.max(avail, buff.length));
						if (read >= 0)
							result += new String(buff);
					} else {
						try {
							Thread.sleep(10);
						} catch (InterruptedException e) {
						}
					}
				}
				/*
				 * while ((line = buffRd.readLine()) != null && !finished) {
				 * result += line + "\n";
				 * 
				 * }
				 */
				finished = true;
				inpStr.close();

			} catch (Exception e) {
				System.out.println(e);
			}
		}
	}

	private static void readStreamInBackground(String name,
			final InputStream pIn, final int bufferSize) {
		new Thread(name) {
			private final Reader r = new BufferedReader(new InputStreamReader(
					pIn), bufferSize);
			private final InputStream _in = pIn;
			private Writer out = new OutputStreamWriter(System.out);
			{
				setDaemon(true);
			}

			public void run() {
				char[] buf = new char[bufferSize];
				int read = 0;
				while (read >= 0) {
					try {
						int avail = _in.available();
						if (avail > 0) {
							read = r.read(buf, 0, Math.min(avail, buf.length));

							if (read > 0) {
								out.write(buf, 0, read);
								out.flush();
							}
						} else {
							// Fix for Java 6u10 on Windows were in.read causes
							// //100% CPU usage<br />
							// when javaw processs launched (not java)<br />
							// most likely there is some spin locking going on
							// and //in.read is<br />
							// blocking to outside world but executing loop
							// internally<br />
							try {
								Thread.sleep(10);
							} catch (InterruptedException e) {
							}
						}
					} catch (IOException e) {
						System.err.println("Error reading sub-process stream: "
								+ e.getMessage());
						e.printStackTrace(System.err);
					}
				}
			}
		}.start();
	}

	public String execute(PollInfo pollInfo) {
		String result = null;
		long timeout;
		HashMap<String, String> params = pollInfo.getNetService().getParams();
		if (params.get("timeout") == null)
			timeout = 10000;
		else
			timeout = Long.parseLong(params.get("timeout")) * 1000;

		String commandOptions = tokenOptionHandler(
				params.get("command-options"), pollInfo);

		String line = prefix + " " + path + " " + commandOptions;
		// ////////////////////TEST/////////////////////////
		// Process process;
		// StreamHandler errorHandler, inputHandler;
		// try {
		// process = Runtime.getRuntime().exec(line);
		//
		// // define 2 concurent stream handler for errorStream and inputStream
		// // otherwise the process may be blocked forever deal to memory leak
		// //readStreamInBackground("System.out", process.getInputStream(),
		// 512);
		// //readStreamInBackground("System.err", process.getErrorStream(),
		// 512);
		//
		// errorHandler = new StreamHandler(process.getErrorStream(), "ERROR");
		// inputHandler = new StreamHandler(process.getInputStream(), "INPUT");
		// // mark the starting time
		// long startTime = Calendar.getInstance().getTimeInMillis();
		// errorHandler.start();
		// inputHandler.start();
		//
		// // errorHandler.finished=true;
		// // inputHandler.finished=true;
		// // wait until finish or timeout
		// while (true) {
		// try {
		// Thread.sleep(50);
		// } catch (InterruptedException e) {
		// // TODO Auto-generated catch block
		// }
		// long currentTime = Calendar.getInstance().getTimeInMillis();
		// if (currentTime - startTime > timeout) {
		//
		// errorHandler.finished = true;
		// inputHandler.finished = true;
		// ;
		// }
		// if (errorHandler.finished && inputHandler.finished) {
		// result = inputHandler.result;
		// break;
		// }
		// }
		// result = inputHandler.result;
		// inputHandler.interrupt();
		// errorHandler.interrupt();
		// process.destroy();
		// // get the result from the inputStream of the process through
		// // inputHandler
		// } catch (IOException e) {
		//
		// }

		// //////////////////////////////////////////////////
		CommandLine cmdLine = CommandLine.parse(line);

		DefaultExecutor executor = new DefaultExecutor();
		int[] exitValues = { 0, 1, 2, 3 };
		executor.setExitValues(exitValues);
		ExecuteWatchdog watchdog = new ExecuteWatchdog(timeout);
		StringOutputStream op = new StringOutputStream(), err = new StringOutputStream();

		PumpStreamHandler pump = new PumpStreamHandler(op, err);

		executor.setWatchdog(watchdog);
		executor.setStreamHandler(pump);
		try {
			executor.execute(cmdLine);
			result = op.getString();
			op.close();
			err.close();
		} catch (ExecuteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return result;
	}

	public String tokenOptionHandler(String options, Object object) {
		StringBuffer cmd = new StringBuffer(options);
		while (cmd.indexOf("%") != -1) {
			int first = cmd.indexOf("%");
			int second = cmd.indexOf("%", first + 1);
			if (first != -1 && second != -1) {
				cmd.replace(
						first,
						second + 1,
						TokenReplace.replaceToken(
								cmd.substring(first + 1, second), object));
			}
		}
		options = cmd.toString();
		return options;
	}
}
